The Basics Of Image Filtering

Feymour/JapoTek

Introduction

Hiya, readers! I've spent the last year working on bitmap images and image filtering, now I have the presumption to say that I've a good knowledge of the matter and I think it's time for you to become acquainted with this nice subject of Computer Graphics. In my humble opinion, Image Filtering offers quite a large amount of subjects to investigate on, in this little text I will only introduce you to this topic and help you to do the first step.

I will not begin from scratch, I assume you all know what an image is, what pixels are, a good knowledge of statistics and know how to work with bitmaps (at least how to blend two colours together, change the colours of a pixel, reach the [x;y] in a image and so on..., all basic stuff IMHO).

Anyway, I don't think this is advanced coding stuff, neither is it difficult or "esoteric": it's pretty basic and well-known by all the "experienced" coders, but I think it could be useful for the newbies of digital image processing.

What is a filter and different kinds of them

With the word "filter" I mean an operation on a bidimensional image that alters the colours or the positions of pixels. Starting from this brief definition we could seperate filters into three big groups:

        - Filters that work on the colour of pixels
        - Filters that work on the position of pixels
        - A mix of the two

To help you understand these categories I will give you some examples. Blur (smooth the image removing some noise, just like an unfocused photograph), sharpen (exalt the contour of the image), emboss (give a three dimensional feeling to the image) and so on, fit perfectly into the first category; pinch, punch, wave, twirl, ripple and those kind of operations are in the second one.

In this minimal little primer on image filtering I will explain only the first family and in particular the so called "matrix" filter, but there are tons of other kinds and other methods of filtering that I can talk about, if you really care about them.

The weighted filter

The weighted filter (also called, in a less accurate way, "matrix" filters) is a really common technique used in image manipulation, most people use it without knowing that is a "standard" method, i.e. more and more coders do a blur (smoothing) routine in the following fashion:

        for ( each pixel )
                current_pixel_colour = average of surrounding pixels' colours

Well, in fact this is nothing more than a optimized and less flexible version of a weighted filter. It is often called a "matrix" filter because it needs a table of numbers, similar to a matrix. Let's see how it works.

We have a table of numbers (could be of any size, square or rectangular, but the most common sizes are 3x3 or 5x5), a division factor and a bias value. Now we must "center" the values' grid on the current pixel (i.e. in a 3x3 grid the value at 2,2 corresponds to the pixel at the current position), and now we must multiply the value in the grid with the corresponding pixel on the image, do this for each pixel, and add them together. Ok, divide the result by the division factor and add the bias value. Now we can store the final pixel value in a new image (we must work on two different images!) at position [x,y]. As you can see, this is quite an heavy task, and it's really slow for using it in realtime applications, but starting from a weighted filter you can always get to a particular case and get rid of all the useless operations. (Using a 5x5 filter on a image we have to do 5*5=25 multiplications + 1 division + 25 adds + 1 add - the bias value - for each pixel or component if you're working in a hicolor-truecolor mode! But using an optimized version for a particular case we could always choose the right values and proportion which could help in the pursuit of speed and realtime applications of the filter.)

In a nutshell, the weighted filter works in this way: the numbers in the table are the weight of each colour in the final result, the higher they are higher the importance of the corresponding pixel colour in the final mixed colour.

From a statistical point of view we are doing a weighted average of colours, in fact often the division factor is a sum of the single weights. I hope you understand my point. Every pixel has a weight in the final mixing that is:

                FinalPixelWeight = PixelWeight / DivisionFactor

If the division factor is greater than the sum of the weights the image will get darker and viceversa. The bias value is a brightness correction factor, in some filters (i.e.: blur) the images tends to get darker, using a small bias value you will maintain the brightness of the initial image.

So, simple? Uh?

Some pseudocode

The following pseudo(C)code is of course untested, unoptimized and it doesn't worry about memory boundaries, data types and image colour space or pixel format. I think you'll be enough clever to code it by yourself.

        for (each pixel in the image)
        {
            gridCounter=0;
            final = 0;

            for(y2=-2; y2<=2; y2++)       // and for each pixel around our
                       for(x2=-2; x2<=2; x2++)    //  current pixel
                   {
                          // add to our total
                  final += source[x+x2][y+y2] * filterTable[gridCounter];
                  // go to the next value on the filter table
                      gridCounter++;
               }

            final /= divisionFactor;
                        final += biasValue;

            destination[x][y] = final;
                }

It's pretty much like that.

Example weighted filters

There are some examples of weighted filters values, I use a 5x5 grid but in some cases also a 3x3 table will do the work.

Smoothing filters:

        Soften (medium)

                0 0 0 0 0
                0 1 3 1 0
                0 3 9 3 0
                0 1 3 1 0       bias:   1
                0 0 0 0 0       divide: 25

        Soften (a lot)

                1 1 1 1 1
                1 1 1 1 1
                1 1 1 1 1
                1 1 1 1 1       bias: 0
                1 1 1 1 1       divide: 25

        Soften (a little)

                0  1  2  1  0
                1  3  10 3  1
                2  10 90 10 2
                1  3  10 3  1   bias:   1
                0  1  2  1  0   divide: maybe 154

Sharpen filters:

        Sharpen (low) [negative values are useful for creating contrast]

                 0  0  0  0  0
                 0 -1 -3 -1  0
                 0 -3 41 -3  0
                 0 -1 -3 -1  0  bias:    0
                 0  0  0  0  0  divide: 25

        Sharpen (medium)

                -1 -1 -1 -1 -1
                -1 -1 -1 -1 -1
                -1 -1 49 -1 -1
                -1 -1 -1 -1 -1  bias:    0
                -1 -1 -1 -1 -1  divide: 25

Emboss filter:

                 0 -1  0
                 1  1 -1    bias:   0
                 0  1  0    divide: 1

(You can change the direction by switching signs and the strength by increasing values.)

Last words Ok, I think that's enough for now. I hope you liked this text and learned something, excuse me if I didn't explain in a clear enough way or if my pseudo code doesn't run. I apologize for every inconvenience.

I wanna make it clear that this is only a little portion of the huge field of image filtering, I can't cover every topic and I think I can't even learn every little aspect of it. But I hope to have introduced you to this interesting matter.

Now, give a ring to your girlfriend, bring a good bottle of Italian wine and make love until tomorrow morning.

Life is too short to code image filtering routines, there's Photoshop, isn't there?

Best wishes,

- Feymour aka Simone Ghiaroni [ghiaroni@programmers.net]